package com.atguigu.gmall.search.service.impl;

import com.atguigu.gmall.model.search.Goods;
import com.atguigu.gmall.model.search.SearchAttr;
import com.atguigu.gmall.model.search.vo.SearchParamsVO;
import com.atguigu.gmall.model.search.vo.SearchResultVo;
import com.atguigu.gmall.search.repository.GoodsRepository;
import com.atguigu.gmall.search.service.GoodsService;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class GoodsServiceImpl implements GoodsService {
    @Autowired
    GoodsRepository goodsRepository;


    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;


    @Override
    public void save(Goods goods) {
        goodsRepository.save(goods);
    }

    @Override
    public void delete(Long skuId) {
        goodsRepository.deleteById(skuId);
    }

    @Override
    public SearchResultVo search(SearchParamsVO param) {
        //构建Dsl
        Query query = buildDsl(param);
        //执行dsl语句
        SearchHits<Goods> searchHits = elasticsearchRestTemplate.search(query, Goods.class,
                IndexCoordinates.of("goods"));
        //封装结果集
        SearchResultVo resultVo = buildResult(param, searchHits);
        return resultVo;


    }

    @Override
    public void incrHotScore(Long skuId, Long hotScore) {
        //构建修改的内容
        Document document = Document.create();
        document.put("hotScore", hotScore);
        //构建修改
        UpdateQuery updateQuery = UpdateQuery.builder(skuId.toString())
                .withDocAsUpsert(true)//开启局部更新
                //需要修改的文档
                .withDocument(document).build();
        //执行更新
        elasticsearchRestTemplate.update(updateQuery, IndexCoordinates.of("goods"));
    }

    private SearchResultVo buildResult(SearchParamsVO param, SearchHits<Goods> searchHits) {
        SearchResultVo searchResultVo = new SearchResultVo();
        //====1、所有参数：回显；
        searchResultVo.setSearchParam(param);

        //===2、面包屑：回显====================
        //品牌面包屑，代表当时检索传入的品牌参数是什么  显示：品牌：小米(mi)
        if (StringUtils.hasText(param.getTrademark())) {
            searchResultVo.setTrademarkParam("品牌：" + param.getTrademark().split(":")[1]);
        }
        //平台属性面包屑：回显之前用了哪些属性进行筛选
        // 屏幕尺寸：超大屏（超过6.6英寸）运行内存：16GBCPU品牌：骁龙（Snapdragon)
        if (param.getProps() != null && param.getProps().length > 0) {
            List<SearchAttr> collect = Arrays.stream(param.getProps()).map(item -> {
                SearchAttr searchAttr = new SearchAttr();
                //截取字符串
                String[] split = item.split(":");
                searchAttr.setAttrId(Long.parseLong(split[0]));
                searchAttr.setAttrValue(split[1]);
                searchAttr.setAttrName(split[2]);
                return searchAttr;
            }).collect(Collectors.toList());
            searchResultVo.setPropsParamList(collect);
        }


        //===3、二次检索区：  所有条件： 筛选 ===========
        //品牌列表; 符合当前检索条件的所有商品涉及到的所有品牌。 进行二次检索
        Terms tmIdAggs = searchHits.getAggregations().get("tmIdAggs");
        List<SearchResultVo.Trademark> trademarkList = tmIdAggs.getBuckets().stream().map(bucket -> {
            //创建品牌信息
            SearchResultVo.Trademark trademark = new SearchResultVo.Trademark();
            //获取聚合信息的key，拿到id
            Long tmId = bucket.getKeyAsNumber().longValue();
            //获取id下面的子聚合拿到tmName信息
            Terms tmNameAggs = bucket.getAggregations().get("tmNameAggs");
            //获取子聚合的key
            String tmName = tmNameAggs.getBuckets().get(0).getKeyAsString();
            //获取id下面的子聚合拿到tmLogoUrl信息
            Terms tmLogoUrlAggs = bucket.getAggregations().get("tmLogoUrlAggs");
            //获取子聚合的key
            String tmLogoUr = tmLogoUrlAggs.getBuckets().get(0).getKeyAsString();
            trademark.setTmId(tmId);
            trademark.setTmName(tmName);
            trademark.setTmLogoUrl(tmLogoUr);
            return trademark;
        }).collect(Collectors.toList());
        searchResultVo.setTrademarkList(trademarkList);
        //属性列表： 符合当前检索条件的所有商品涉及到的所有属性。 进行二次检索
        ParsedNested nestedAggs = searchHits.getAggregations().get("nestedAggs");
        Terms attrIdAggs = nestedAggs.getAggregations().get("attrIdAggs");
        List<SearchResultVo.Attr> attrList = attrIdAggs.getBuckets().stream().map(item -> {
            //创建需要映射的对象
            SearchResultVo.Attr attr = new SearchResultVo.Attr();
            //获取id
            long attrId = item.getKeyAsNumber().longValue();
            Terms attrNameAggs = item.getAggregations().get("attrNameAggs");
            Terms.Bucket attrNameBucket = attrNameAggs.getBuckets().get(0);
            String attrName = attrNameBucket.getKeyAsString();
            Terms attrValuesAggs = item.getAggregations().get("attrValuesAggs");
            List<String> attrValueList = attrValuesAggs.getBuckets().stream().map(Terms.Bucket::getKeyAsString)
                    .collect(Collectors.toList());

            attr.setAttrId(attrId);
            attr.setAttrName(attrName);
            attr.setAttrValueList(attrValueList);
            return attr;
        }).collect(Collectors.toList());
        searchResultVo.setAttrsList(attrList);

        //解析数据

        List<Goods> goodsList = searchHits.getSearchHits().stream().map(item -> {
            Goods content = item.getContent();
            if (item.getHighlightField("title") != null && item.getHighlightField("title").size() != 0) {
                content.setTitle(item.getHighlightField("title").get(0));
            }
            return content;
        }).collect(Collectors.toList());
        //===4、商品数据区：检索到的所有商品
        searchResultVo.setGoodsList(goodsList);

        //===5、排序、分页信息区
        //页面显示排序效果
        SearchResultVo.OrderMap orderMap = new SearchResultVo.OrderMap();
        orderMap.setSort(param.getOrder().split(":")[1]);
        orderMap.setType(param.getOrder().split(":")[0]);
        searchResultVo.setOrderMap(orderMap);

        searchResultVo.setPageNo(param.getPageNo());
        //数据查询的总记录数
        long totalHits = searchHits.getTotalHits();
        //计算总页数
        double ceil = Math.ceil(totalHits * 1.0 / param.getPageSize());
        searchResultVo.setTotalPages((long) ceil);

        //待拼接的参数
        //方法的局部变量是没有线程安全问题；只有类的成员变量，由于单线程，导致多线程共享操作，就会有安全问题
        StringBuilder urlParam = buildParamUrl(param);
        searchResultVo.setUrlParam(urlParam.toString());
        return searchResultVo;

    }

    private static StringBuilder buildParamUrl(SearchParamsVO param) {
        StringBuilder urlParam = new StringBuilder("list.html?");
        if (param.getCategory1Id() != null) {
            urlParam.append("&category1Id=" + param.getCategory1Id());
        }
        if (param.getCategory2Id() != null) {
            urlParam.append("&category2Id=" + param.getCategory2Id());
        }
        if (param.getCategory3Id() != null) {
            urlParam.append("&category3Id=" + param.getCategory3Id());
        }
        if (StringUtils.hasText(param.getKeyword())) {
            urlParam.append("&keyword=" + param.getKeyword());
        }
        if (StringUtils.hasText(param.getTrademark())) {
            urlParam.append("&trademark=" + param.getTrademark());
        }
        if (param.getProps() != null && param.getProps().length > 0) {
            for (String prop : param.getProps()) {
                urlParam.append("&props=" + prop);
            }
        }
        return urlParam;
    }

    /**
     * // category3Id=61&trademark=2:华为&props=23:256G:机身存储
     * // &props=24:6寸:屏幕尺寸&props=33:12G:机身内存&keyword=手机&order=1:asc&pageNo=3
     *
     * @param param
     * @return
     */
    private Query buildDsl(SearchParamsVO param) {
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        List<QueryBuilder> must = boolQuery.must();
        //构建三级分类的条件查询
        if (param.getCategory1Id() != null) {
            must.add(QueryBuilders.termQuery("category1Id", param.getCategory1Id()));
        }
        if (param.getCategory2Id() != null) {
            must.add(QueryBuilders.termQuery("category2Id", param.getCategory2Id()));
        }
        if (param.getCategory3Id() != null) {
            must.add(QueryBuilders.termQuery("category3Id", param.getCategory3Id()));
        }
        //构建品牌的条件查询
        String trademark = param.getTrademark();
        if (StringUtils.hasText(trademark)) {
            String[] split = trademark.split(":");
            must.add(QueryBuilders.termQuery("tmId", split[0]));
        }
        //构建关键字搜索的条件查询
        if (StringUtils.hasText(param.getKeyword())) {
            must.add(QueryBuilders.matchQuery("title", param.getKeyword()));
        }
        //构建平台属性的条件查询
        if (param.getProps() != null && param.getProps().length > 0) {
            for (String prop : param.getProps()) {
                String[] propSplit = prop.split(":");
                BoolQueryBuilder nestedBool = QueryBuilders.boolQuery();
                List<QueryBuilder> nestedMust = nestedBool.must();
                nestedMust.add(QueryBuilders.termQuery("attrs.attrId", propSplit[0]));
                nestedMust.add(QueryBuilders.termQuery("attrs.attrValue", propSplit[1]));
                must.add(QueryBuilders.nestedQuery("attrs", nestedBool, ScoreMode.None));
            }
        }

        //把查询放入query对象
        NativeSearchQuery query = new NativeSearchQuery(boolQuery);


        //如果有排序信息，构建排序规则
        if (StringUtils.hasText(param.getOrder())) {
            String[] orderSplit = param.getOrder().split(":");
            Sort sort = null;
            switch (orderSplit[0]) {
                case "1":

                    sort = Sort.by("hotScore");
                    break;
                case "2":
                    sort = Sort.by("price");
                    break;
            }
            sort = "asc".equals(orderSplit[1]) ? sort.ascending() : sort.descending();
            query.addSort(sort);
        }

        //构建分页信息
        query.setPageable(PageRequest.of(param.getPageNo() - 1, param.getPageSize()));
        //构建高亮查询
        if (StringUtils.hasText(param.getKeyword())) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("title");
            highlightBuilder.requireFieldMatch(false);//多个高亮关闭
            highlightBuilder.preTags("<span style='color:red'>");
            highlightBuilder.postTags("</span>");
            query.setHighlightQuery(new HighlightQuery(highlightBuilder));
        }
        //构建品牌聚合查询
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders
                .terms("tmIdAggs").field("tmId").size(100)
                //tmNameAggs子聚合
                .subAggregation(AggregationBuilders.terms("tmNameAggs")
                        .field("tmName").size(1))
                //tmLogoUrlAggs子聚合
                .subAggregation(AggregationBuilders.terms("tmLogoUrlAggs")
                        .field("tmLogoUrl").size(1));
        query.addAggregation(termsAggregationBuilder);
        //构建平台属性查询,缺点可读性不高
//        NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders.nested("nestedAggs", "attrs")
//                .subAggregation(
//                        AggregationBuilders.terms("attrIdAggs")
//                                .field("attrs.attrId").size(100).subAggregation(
//                                        AggregationBuilders.terms("attrNameAggs")
//                                                .field("attrs.attrName").size(1).subAggregation(
//                                                        AggregationBuilders.terms("attrValuesAggs")
//                                                                .field("attrs.attrValue").size(100)
//                                                )
//                                )
//                );
        //构建嵌套聚合
        NestedAggregationBuilder nestedAggs = AggregationBuilders.nested("nestedAggs", "attrs");
        //构建attrId子聚合
        TermsAggregationBuilder attrIdAggs = AggregationBuilders.terms("attrIdAggs")
                .field("attrs.attrId").size(200);
        //构建attrName子聚合
        TermsAggregationBuilder attrNameAggs = AggregationBuilders.terms("attrNameAggs")
                .field("attrs.attrName").size(1);
        //构建attrValue子聚合
        TermsAggregationBuilder attrValuesAggs = AggregationBuilders.terms("attrValuesAggs")
                .field("attrs.attrValue").size(100);
        attrIdAggs.subAggregation(attrValuesAggs);
        attrIdAggs.subAggregation(attrNameAggs);
        nestedAggs.subAggregation(attrIdAggs);


        query.addAggregation(nestedAggs);
        return query;

    }
}



